//////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <Core/Core.hpp>
#include <chrono>
#include <thread>
#include <SFML/Graphics.hpp>
#include <SFML/OpenGL.hpp>
#include <SFML/Window.hpp>
#include <Config/Config.hpp>

#include <TweakUtils/TweakPanel.hpp>

// Begin testing Rocket.
#include <Rocket/Core.h>
#include <Rocket/Controls.h>
#include <Rocket/Debugger.h>

#include <SystemInterfaceSFML.h>
#include <RenderInterfaceSFML.h>


//////////////////////////////////////////////////////////////////////////
CONFIG_VAR( uint32_t, Width, "Screen width.", 640 );
CONFIG_VAR( uint32_t, Height, "Screen height.", 480 );
CONFIG_VAR( uint32_t, Left, "Screen left.", 0 );
CONFIG_VAR( uint32_t, Top, "Screen top.", 0 );
CONFIG_VAR( bool, Fullscreen, "Fullscreen toggle.", false );
CONFIG_VAR( int32_t, FullScreenMode, "Resolution for fullscreen.", 0 );

TWEAKABLE_VAR( static const, Math::Vector3f, ClearColor, "Color to clear the window to.", Math::Vector3f( 0.0f, 0.5f, 1.0f ) );
TWEAKABLE_VAR( static const, bool, DisplayRocketDebugger, "Rocket debugger on/off.", false );

class TestWindow : public sf::RenderWindow
{
public:
	TestWindow(
		sf::VideoMode				mode,
		const sf::String&			title,
		sf::Uint32					style		=	sf::Style::Default,
		const sf::ContextSettings&	settings	=	sf::ContextSettings()
		)
	:	sf::RenderWindow( mode, title, style, settings )
	{
		if( Fullscreen )
			setPosition( sf::Vector2i( 0, 0 ) );
		else
			setPosition( sf::Vector2i( Left, Top ) );
	}

	virtual ~TestWindow()
	{
	}

	// Hide the base class implementation.
	void				close()
	{
		if( !Fullscreen )
			storePosition();
		sf::Window::close();
	}

	virtual void		onCreate() override
	{
		if( Fullscreen )
			setPosition( sf::Vector2i( 0, 0 ) );
		else
			setPosition( sf::Vector2i( Left, Top ) );
	}

	virtual void		onResize() override
	{
		sf::Vector2u	size	=	getSize();
		Width	=	size.x;
		Height	=	size.y;
	}

	void				storePosition()
	{
		if( !Fullscreen )
		{
			sf::Vector2i	pos		=	getPosition();
			Left	=	pos.x;
			Top		=	pos.y;
		}
	}

private:
};


void LoadFonts()
{
	Rocket::Core::String font_names[4];
	font_names[0] = "Delicious-Roman.otf";
	font_names[1] = "Delicious-Italic.otf";
	font_names[2] = "Delicious-Bold.otf";
	font_names[3] = "Delicious-BoldItalic.otf";

	for (int i = 0; i < sizeof(font_names) / sizeof(Rocket::Core::String); i++)
	{
		Rocket::Core::FontDatabase::LoadFontFace( font_names[i] );
	}
}


int main( int argc, char** argv )
{
	google::ParseCommandLineFlags( &argc, &argv, true );
#if !defined( OS_WINDOWS )
	google::InstallFailureSignalHandler();
#endif
	google::LogToStderr();
	google::InitGoogleLogging( argv[ 0 ] );

	int result	=	0;
	{
		Config::Initializer					configInit( "TestSfml.json" );

//		sf::VideoMode						desktop		=	sf::VideoMode::getDesktopMode();
		const std::vector<sf::VideoMode>&	fsModes		=	sf::VideoMode::getFullscreenModes();

		//////////////////////////////////////////////////////////////////////////
		TestWindow window(
			sf::VideoMode( Width, Height, 32 ),
			"SFML Window",
			sf::Style::Default | (Fullscreen ? sf::Style::Fullscreen : 0),
			sf::ContextSettings( 32 )
		);
		window.setVerticalSyncEnabled( true );

		// Force glew init via SFML.
		sf::Shader::isAvailable();

		// Init AntTweakBar.
		TwInit( TW_OPENGL, NULL );

		{
			bool	screenResize		=	false;
			int32_t	fullscreenChange	=	0;

			struct TestStruct
			{
				uint32_t		mTest1;
				int32_t			mTest2;
				uint16_t		mTest3;
				int16_t			mTest4;
			}	testStruct	=	{1, 2, 3, 4};

#ifdef ENABLE_DEVBUILD
			// Test the structure builder.
			TweakUtils::Panel::StructType_t		structType	=	TweakUtils::Panel::StructType< TestStruct >( "TestStruct" )
				.Member< uint32_t >( "mTest1", offsetof( TestStruct, mTest1 ) )
				.Member< int32_t >( "mTest2", offsetof( TestStruct, mTest2 ) )
				.Member< uint16_t >( "mTest3", offsetof( TestStruct, mTest3 ) )
				.Member< int16_t >( "mTest4", offsetof( TestStruct, mTest4 ) );

			//////////////////////////////////////////////////////////////////////////
			// Test the new enum builder.
			TweakUtils::Panel::EnumBuilder		enumBuilder	=	TweakUtils::Panel::EnumType( "VideoModes" );
			for( auto it : fsModes )
			{
				enumBuilder.Entry( std::to_string( it.width ) + "x" + std::to_string( it.height ) + ":" + std::to_string( it.bitsPerPixel ) );
			}
			TweakUtils::Panel::EnumType_t		enumType	=	enumBuilder;
#endif

			//////////////////////////////////////////////////////////////////////////
#if defined( ENABLE_DEVBUILD )
			TweakUtils::Panel	testPanel	=
				TweakUtils::Panel::Create( "Test Panel" )
					.LightText()
					.Variable< uint32_t >( "Window Position Left", &Left ).End()
					.Variable< uint32_t >( "Window Position Top", &Top ).End()
					.Variable< uint32_t >( "Screen Width", &Width ).Callback( [&]{screenResize = true;} ).End()
					.Variable< uint32_t >( "Screen Height", &Height ).Callback( [&]{screenResize = true;} ).End()
					.Separator( "Fullscreen Settings" ).End()
					.Enumeration( "Fullscreen Video Mode", enumType, &FullScreenMode ).End()
					.Variable< bool >( "Fullscreen", &Fullscreen ).Callback( [&]{fullscreenChange = 15;} ).End()
					.Separator( "Settings" ).End()
					.Color3f( "Clear Color", &ClearColor ).Opened().End()
					.Separator( "Test Settings" ).End()
					.Button( "Test Button", [&]{ LOG( INFO ) << "Button pressed."; } ).End()
					.Struct( "Test Struct", structType, &testStruct ).End();
#endif

			//////////////////////////////////////////////////////////////////////////
			RocketSFMLSystemInterface*	systemInterface	=	new RocketSFMLSystemInterface();
			RocketSFMLRenderer*			renderInterface	=	new RocketSFMLRenderer();

			Rocket::Core::SetSystemInterface( systemInterface );
			Rocket::Core::SetRenderInterface( renderInterface );
			renderInterface->SetWindow( &window );

			if( Rocket::Core::Initialise() )
			{
				LoadFonts();

				Rocket::Core::Context*			context		=	Rocket::Core::CreateContext( "default", Rocket::Core::Vector2i( Width, Height ) );
				Rocket::Core::ElementDocument*	doc			=	context->LoadDocument( "demo.rml" );

				if( doc!=nullptr )
				{
					doc->Show();
					doc->RemoveReference();
				}

				Rocket::Debugger::Initialise( context );
				Rocket::Debugger::SetVisible( DisplayRocketDebugger );

				while( window.isOpen() )
				{
					if( screenResize )
					{
						LOG( INFO ) << "Resizing screen to: " << *(&Width) << " x " << *(&Height);
						screenResize	=	false;

						window.setSize( sf::Vector2u( Width, Height ) );
					}

					// Process events
					sf::Event event;
					while( window.pollEvent( event ) )
					{
						// Push input to AntTweakBar.
						int	handled	=	TwEventSFML( &event, 2, 0 );

						if( !handled )
						{
							// Close window : exit
							if( event.type == sf::Event::Closed )
								window.close();

							// Escape key : exit
							if( (event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Escape) )
								window.close();

	#if defined( ENABLE_DEVBUILD )
							if( (event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::F5) )
								testPanel.Toggle();
	#endif

							switch(event.type)
							{
							case sf::Event::Resized:
								renderInterface->Resize();
								break;
							case sf::Event::MouseMoved:
								context->ProcessMouseMove( event.mouseMove.x, event.mouseMove.y, systemInterface->GetKeyModifiers( &window ) );
								break;
							case sf::Event::MouseButtonPressed:
								context->ProcessMouseButtonDown( event.mouseButton.button, systemInterface->GetKeyModifiers( &window ) );
								break;
							case sf::Event::MouseButtonReleased:
								context->ProcessMouseButtonUp( event.mouseButton.button, systemInterface->GetKeyModifiers( &window ) );
								break;
							case sf::Event::MouseWheelMoved:
								// Invert the wheel movement.
								context->ProcessMouseWheel( -event.mouseWheel.delta, systemInterface->GetKeyModifiers( &window ));
								break;
							case sf::Event::TextEntered:
								if( event.text.unicode > 32 )
									context->ProcessTextInput( event.text.unicode );
								break;
							case sf::Event::KeyPressed:
								context->ProcessKeyDown( systemInterface->TranslateKey( event.key.code), systemInterface->GetKeyModifiers( &window ) );
								break;
							DEVBUILD(
								case sf::Event::KeyReleased:
									if( event.key.code == sf::Keyboard::F8)
									{
										DisplayRocketDebugger	=	DisplayRocketDebugger ? false : true;
										Rocket::Debugger::SetVisible( DisplayRocketDebugger );
									};
									context->ProcessKeyUp( systemInterface->TranslateKey( event.key.code ), systemInterface->GetKeyModifiers( &window ) );
									break;
								)
							};
						}
					}

					// Debounce the toggle, ant tweak bar seems to toggle the bool
					// twice if the change is applied immediately.  So we wait a couple
					// frames before actually applying the change.
					if( fullscreenChange>0 )
					{
						std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
						if( --fullscreenChange==0 )
						{
							if( Fullscreen )
							{
								window.storePosition();
								window.setPosition( sf::Vector2i( 0, 0 ) );
								window.create(
									sf::VideoMode( fsModes[ FullScreenMode ].width, fsModes[ FullScreenMode ].height, fsModes[ FullScreenMode ].bitsPerPixel ),
									"SFML Window",
									sf::Style::Default | (Fullscreen ? sf::Style::Fullscreen : 0),
									sf::ContextSettings( 32 )
									);
							}
							else
							{
								window.create(
									sf::VideoMode( Width, Height, 32 ),
									"SFML Window",
									sf::Style::Default | (Fullscreen ? sf::Style::Fullscreen : 0),
									sf::ContextSettings( 32 )
									);
							}
						}
					}

					window.setActive();
					glClearColor( ClearColor( 0 ), ClearColor( 1 ), ClearColor( 2 ), 0.0f );
					glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );


					TwWindowSize( window.getSize().x, window.getSize().y );
					TwDraw();

					//
					renderInterface->Resize();

					context->Update();
					context->Render();

					window.display();
					configInit.Update();
				}

				doc->GetContext()->UnloadDocument( doc );
				Rocket::Core::SetSystemInterface( nullptr );
				Rocket::Core::SetRenderInterface( nullptr );
				context->RemoveReference();
				Rocket::Core::Shutdown();
			}
		}

		TwTerminate();
	}

	google::ShutdownGoogleLogging();
	return result;
}
